<?php
/**
 * Celebrio FileSystem module
 *
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage FileSystem
 */
namespace Celebrio\FileSystem;

use Celebrio\FileSystem\File;


/**
 * Albireo class simplyfing work with a text file (non-binary)
 *
 * @author     pavel
 */
class TextFile extends File {

    //inherits attributes from File



    /**
     * get the number of bytes from the beginning of the file to the pointer
     * @return int number of bytes
     */
    public function getPointerPosition() {
	$this->savePosition();
	return $this->saved_position;
    }

    /**
     * move to certain position in the file
     * @param int $position The new position, measured in bytes from the beginning of the file
     * @return Returns TRUE on success or FALSE on failure.
     */
    public function movePointerToPosition($position) {
	$this->saved_position = (int)$position;
	$result = 0;
	if ( is_resource($this->file_handler) ) {
	    $result = fseek($this->file_handler, $this->saved_position);
	}
	if ( $result == 0 ) {
	    return TRUE;
	} else {
	    return FALSE;
	}
    }

    /**
     * set position to current saved position plus $number_of_bytes
     * @param int $number_of_bytes number of bytes to skip
     * @return Returns TRUE on success or FALSE on failure.
     */
    public function skipBytes($number_of_bytes) {
	$result = 0;
	if ( is_resource($this->file_handler) ) {
	    $result = fseek($this->file_handler, (int)$number_of_bytes, SEEK_CUR);
	    $this->savePosition();
	}
	$this->saved_position = (int)$number_of_bytes;
	if ( $result == 0 ) {
	    return TRUE;
	} else {
	    return FALSE;
	}
    }

    /**
     * moves the file pointer to a certain line (depends on file mode)
     * @param int $number_of_line number of line we want to read next
     * @return the number of bytes from the beginning of the file to the pointer
     */
    public function movePointerToLine($number_of_line) {
	$this->openFile(); // opens file if it hasn't been opened yet
	--$number_of_line; // we want to read that line so we have to discount one line from the number
	rewind($this->file_handler);
	while($number_of_line > 0 && !feof($this->file_handler)){
	    fgets($this->file_handler);
	    --$number_of_line;
	}
	$this->savePosition();
	return $this->getPointerPosition();
    }

    /**
     * skip a certain number of lines (depends on file mode)
     * @param int $number_of_line number of line we want to read next
     * @return the number of bytes from the beginning of the file to the pointer
     */
    public function skipLines($number_of_line) {
	$this->openFile(); // opens file if it hasn't been opened yet
	while($number_of_line > 0 && !feof($this->file_handler)){
	    fgets($this->file_handler);
	    --$number_of_line;
	}
	$this->savePosition();
	return $this->getPointerPosition();
    }


    /**
     * get a character from file (depends on file mode)
     * @return mixed return the character or FALSE if the end of file has been reached
     */
    public function getCharacter () {
	$this->openFile(); // opens file if it hasn't been opened yet
	if ( !feof($this->file_handler) ) {
	    return fgetc ( $this -> file_handler );
	} else {
	    return FALSE;
	}
    }

    /**
     * get a line from file (depends on file mode)
     * @param int $length Reading ends when length - 1 bytes have been read, on a newline (which is included in the return value), or on EOF (whichever comes first). If no length is specified, it will keep reading from the stream until it reaches the end of the line.
     * @return mixed return the line or FALSE if the end of file has been reached
     */
    public function getLine ( $length = NULL ) {
	$this->openFile();
	if ( !feof($this->file_handler) ) {
	    if ( $length === NULL ) {
		return fgets ( $this -> file_handler );
	    } else {
		return fgets ( $this -> file_handler, $length );
	    }
	} else {
	    return FALSE;
	}
    }

    /**
     * Reads WHOLE current line, also with the characters before the file pointer
     * 
     * @return <type>
     */
    public function getCurrentLine() {
        $this->movePointerBeginningCurrent();
        return $this->getLine();
    }

    /**
     * Moves the file pointer to the beginning of the current line (EOL or EOF is
     * counted as the last character at the line).
     *
     * The first if-clause checks whether the pointer is right at the end of the line
     * and if yes, jumps to the previous character. This should not lead to the erroneous
     * behaviour when empty lines are present, but it's not tested (and I'm
     * too tired to prove it).
     */
    public function movePointerBeginningCurrent() {
        if ($this->get(1) == "\n") {
            $this->movePointerToPosition($this->getPointerPosition()-1);
        }
        $this->movePointerToPosition($this->getPointerPosition()-1);
        while ($this->get(1) != "\n") {
            $this->movePointerToPosition($this->getPointerPosition()-2);
            if ($this->getPointerPosition() <= 0) break;
        }
    }

    /**
     * get a line from file and strip HTML elements (depends on file mode)
     * @param int $length Reading ends when length - 1 bytes have been read, on a newline (which is included in the return value), or on EOF (whichever comes first). If no length is specified, it will keep reading from the stream until it reaches the end of the line.
     * @return mixed return the line or FALSE if the end of file has been reached
     */
    public function getLineWithoutHTML ( $length = NULL ) {
	$this->openFile();
	if ( !feof($this->file_handler) ) {
	    if ( $length === NULL ) {
		return fgetss ( $this -> file_handler );
	    } else {
		return fgetss ( $this -> file_handler, $length );
	    }
	} else {
	    return FALSE;
	}
    }


    /**
     * returns file contents as an array. Each element of the array corresponds to a line in the file, with the newline still attached.
     * @return mixed return the array or FALSE upon failure
     */
    public function getFileAsArray () {
	$this->openFile();
	return file($this->file_url);
    }

    /**
     * if the file already exists, append the data to the end of the file (changes mode to a+ or ab+)
     * @param string $data data to write
     * @return mixed returns the number of bytes that were written to the file, or FALSE on failure
     */
    public function append ( $data ) {
	$this->movePointerToPosition($this->getFileSize());
	return $this->put($data);
    }

    /**
     * Truncates the file to a given length
     * @param int $length The size to truncate to.
     * @return boolean Returns TRUE on success or FALSE on failure.
     */
    public function truncate ( $length ) {
	$this->openFile(); // opens file if it hasn't been opened yet
	$result = ftruncate($this->file_handler, $length);
	$this->rewind();
	return $result;
    }

    /**
     * truncates the file to a 0 size
     * @return boolean Returns TRUE on success or FALSE on failure.
     */
    public function clear () {
	$this->openFile(); // opens file if it hasn't been opened yet
	$result = ftruncate($this->file_handler, 0);
	$this->rewind();
	return $result;
    }

}